﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Wise.Util.Queryable;

namespace Wise.Util
{
    public static partial class ObjectExtensions
    {
        internal static string[] DEFAULT_SKIP_FIELD_OR_PROPERTY = new string[] { "Id", "CreatedOnDate", "Created_On_Date", "CreatedByUserId", "Created_By_User_Id"
            ,"Is_Enabled", "IsEnabled", "Enabled","Enable", "EnableMask","IsDeleted", "Is_Deleted","Deleted","DeleteMask" };

        /// <summary>
        /// 浅表复制，将操作对象克隆成为T类型的新对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T CloneTo<T>(this object model)
            where T : new()
        {
            T t = new T();
            CopyTo(model, t);
            return t;
        }

        /// <summary>
        /// 复制实体属性至另外一个实体:浅表复制
        /// 应用场景:请求值转对象，A类对象根据属性、字段复制成B类对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="targetEntity"></param>
        /// <example>new {A="1"}.CopyTo&lt;MyClass&gt;</example>
        /// <remarks>
        /// 会跳过静态字段、只读字段、静态属性、非公共的属性及字段;
        /// 严格匹配名称的大小写及空格！(class A{a;A;} class B{A},那么要复制哪一个值？如果A优先，那么a才是正确值怎么破！！)
        /// </remarks>
        /// <exception cref="InvalidCastException">目标如果是字典，那么目标的Key类型必须实现 IConvertible </exception>
        /// <exception cref="FormatException">源格式与目标格式不匹配,常见于源是字符串、目标是值类型</exception>
        public static void CopyTo<T>(this object source, T targetEntity)
        {
            CopyToObject(source, targetEntity);
        }

        /// <summary>
        /// 复制实体属性至另外一个实体:浅表复制
        /// 应用场景:请求值转对象，A类对象根据属性、字段复制成B类对象
        /// </summary>
        /// <param name="source"></param>
        /// <param name="skipDefaultFields">跳过默认字段 see:ObjectExtensions.DEFAULT_SKIP_FIELD_OR_PROPERTY</param>
        /// <param name="targetEntity"></param>
        /// <param name="toSkip">准备要跳过的属性或字段。区分大小写！</param>
        /// <example>new {A="1"}.CopyTo&lt;MyClass&gt;</example>
        /// <remarks>
        /// 会跳过静态字段、只读字段、静态属性、非公共的属性及字段;
        /// 严格匹配名称的大小写及空格！(class A{a;A;} class B{A},那么要复制哪一个值？如果A优先，那么a才是正确值怎么破！！)
        /// </remarks>
        /// <exception cref="InvalidCastException">目标如果是字典，那么目标的Key类型必须实现 IConvertible </exception>
        /// <exception cref="FormatException">源格式与目标格式不匹配,常见于源是字符串、目标是值类型</exception>
        public static void CopyToObject(this object source, object targetEntity,bool skipDefaultFields=false, params string[] toSkip)
        {
            #region 格式校验
            {
                if (source == null)
                    return;
                if (targetEntity == null)
                    throw new Exception("实体不能为空");
            }
            #endregion
            List<string> skips = new List<string>();
            if (skipDefaultFields)
                skips.AddRange(DEFAULT_SKIP_FIELD_OR_PROPERTY);
            skips.AddRange(toSkip);
            Type sType = source.GetType(), tType = targetEntity.GetType();
            Dictionary<string, Func<object, object>> sourceDatas = new Dictionary<string, Func<object,object>>();
            dynamic dic = null;
            #region getSource
            if (Object.Equals(source, targetEntity))
            {
                return;
            }
            else if (source is IDictionary)
            {
                dic = source as IDictionary;
                foreach (IDictionaryEnumerator kv in dic)
                {
                    sourceDatas.Add(kv.Key.ToString(), (k) => dic[k]);
                }
            }
            else if(source is DataRow)
            {
                DataRow row = (DataRow)source; 
                for (int i = 0; i < row.Table.Columns.Count; i++)
                {
                    var col = row.Table.Columns[i];
                    sourceDatas.Add(col.ColumnName, (cn) => row[cn.ToString()]);
                }
            }
            else if (source is NameValueCollection)
            {
                dic = source as NameValueCollection;
                foreach (string key in dic.AllKeys)
                {
                    sourceDatas.Add(key, (k) => dic[k]);
                }
            }
            else if (source is IList)
            {
                var a1 = sType.GetGenericArguments();
                var a2 = tType.GetGenericArguments();
                if (a1.Length == a2.Length)
                {
                    for (int i = 0; i < a1.Length; i++)
                    {
                        if (a1[i] != a2[i])
                        {
                            return;
                        }
                    }

                    IList tList = (targetEntity as IList);
                    IList sList = (source as IList);
                    foreach (var item in sList)
                    {
                        tList.Add(item);
                    }
                }
                return;
            }
            else
            {
                {
                    var lst = sType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(c => c.CanRead).ToList();
                    foreach (PropertyInfo item in lst)
                    {
                        sourceDatas.Add(item.Name, (k) => item.GetValue(source));
                    }
                }
                {
                    var lst = sType.GetFields(BindingFlags.Instance | BindingFlags.Public).Where(c => !c.IsInitOnly && !c.IsStatic && !c.IsLiteral).ToList();
                    foreach (FieldInfo item in lst)
                    {
                        sourceDatas.Add(item.Name, (k) => item.GetValue(source));
                    }
                }
            }
            #endregion
            dynamic rt = null;
            #region doSet
            if (targetEntity is IDictionary)
            {
                rt = targetEntity as IDictionary;
                var args = tType.GetGenericArguments();
                Type t1 = args[0], t2 = args[1];
                foreach (var item in sourceDatas)
                {
                    object val = item.Value(item.Key);
                    //对象必须实现 IConvertible
                    object k = Convert.ChangeType(item.Key, t1);
                    rt[k] = val == null ? null :
                        val is string ? JsonHelper.ToObject(val.ToString(), t2) :
                        Convert.ChangeType(val, t2);
                }
            }
            else if (targetEntity is NameValueCollection)
            {
                rt = targetEntity as NameValueCollection;
                foreach (var item in sourceDatas)
                {
                    object val = item.Value(item.Key);
                    rt[item.Key] = val == null ? null : val.ToString();
                }
            }
            else
            {
                #region =>Object
                {
                    var lst = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(c => c.CanWrite).ToList();
                    foreach (PropertyInfo item in lst)
                    {
                        if (sourceDatas.Any(x => x.Key == item.Name) && !skips.Contains(item.Name) )
                        {
                            var fd = sourceDatas.FirstOrDefault(x => x.Key == item.Name);
                            object val = fd.Value(fd.Key);
                            val = val.ChangeTo(item.PropertyType);
                            item.SetValue(targetEntity, val);
                        }
                    }
                }
                {
                    var lst = tType.GetFields(BindingFlags.Instance | BindingFlags.Public).Where(c => !c.IsInitOnly && !c.IsStatic && !c.IsLiteral).ToList();
                    foreach (FieldInfo item in lst)
                    {
                        if (sourceDatas.Any(x => x.Key == item.Name) && !skips.Contains(item.Name))
                        {
                            var fd = sourceDatas.FirstOrDefault(x => x.Key == item.Name);
                            object val = fd.Value(fd.Key);
                            val = val.ChangeTo(item.FieldType);
                            Convert.ChangeType(val, item.FieldType);
                            item.SetValue(targetEntity, val);
                        }
                    }
                }
                #endregion
            }
            #endregion
        }

        /// <summary>
        /// 简易转换,会进行浅表复制，对String\值类型额外做默认值处理
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T ChangeTo<T>(this object source)
        {
            return (T)ChangeTo(source, typeof(T));
        }

        /// <summary>
        /// 简易转换,会进行浅表复制，对String\值类型额外做默认值处理
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="changeTo">要转换成的类型</param>
        /// <returns></returns>
        public static dynamic ChangeTo(this object source, Type changeTo)
        {
            if (source == null||source==DBNull.Value)
            {
                #region NULL
                if (changeTo.IsValueType)
                {
                    return ObjectHelper.CreateInstance(changeTo);
                }
                else
                {
                    return null;
                }
                #endregion
            }
            else
            {
                #region OTHER
                Type sourceType = source.GetType();
                if (sourceType == changeTo || sourceType.Unpack() == changeTo.Unpack())
                {
                    changeTo = changeTo.Unpack();
                    object data = null;
                    if (changeTo == typeof(string))
                    {
                        return source.ToString();
                    }
                    else if(changeTo.IsValueType)
                    {
                        //值类型 无需转换
                        return source;// ObjectHelper.CreateInstance(changeTo);
                    }
                    else
                    {
                        //引用类型如果不新建对象，将会直影响源对象
                        data = ObjectHelper.CreateInstance(changeTo);
                    }
                    CopyToObject(source, data);
                    return data;
                }
                else if(sourceType==typeof(DataRow))
                {
                    var data = ObjectHelper.CreateInstance(changeTo);
                    CopyToObject(source, data);
                    return data;
                }
                else
                {
                    sourceType = sourceType.Unpack();
                    changeTo = changeTo.Unpack();
                    if (sourceType.IsValueType)
                    {
                        #region 从值类型转其他
                        if (changeTo.IsValueType)
                        {
                            if(changeTo.IsEnum)
                            {
                                return Enum.Parse(changeTo, source.ToString());
                            }
                            return Convert.ChangeType(source, changeTo);
                        }
                        else if (changeTo == typeof(String))
                        {
                            return source.ToString();
                        }
                        else
                        {
                            var caller = changeTo.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == 1
                             && x.GetParameters()[0].ParameterType.Unpack() == sourceType);
                            if (caller == null)
                                throw new NotImplementedException(String.Format("无法从类型{0}转换成类型{1}", sourceType.FullName, changeTo.FullName));
                            return caller.Invoke(new Object[] { source });
                        }
                        #endregion
                    }
                    else
                    {
                        #region 从其他类型转其他
                        if (changeTo.IsValueType)
                        {
                            if (sourceType == typeof(String))
                            {
                                if (String.IsNullOrEmpty(source as String))
                                {
                                    return ObjectHelper.CreateInstance(changeTo);
                                }
                                return Convert.ChangeType(source, changeTo);
                            }
                            else
                            {
                                throw new NotImplementedException(String.Format("无法从类型{0}转换成类型{1}", sourceType.FullName, changeTo.FullName));
                            }
                        }
                        else
                        {
                            var caller = changeTo.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == 1
                             && x.GetParameters()[0].ParameterType.Unpack() == sourceType);
                            if (caller != null)
                            {
                                return caller.Invoke(new Object[] { source });
                            }
                            else
                            {
                                var data = ObjectHelper.CreateInstance(changeTo);
                                CopyToObject(source, data);
                                return data;
                            }
                        }
                        #endregion
                    }
                }
                #endregion
            }
        }

    }
}
